package com.bb.flow.parser.impl;

import com.bb.flow.api.ConditionValueCalc;
import com.bb.flow.api.ExpressionCalc;
import com.bb.flow.api.FlowNode;
import com.bb.flow.api.exception.ParseException;
import com.bb.flow.config.AbstractConfigNode;
import com.bb.flow.config.ClassInfo;
import com.bb.flow.config.ConditionInfo;
import com.bb.flow.config.FlowInfo;
import com.bb.flow.config.enumtype.NodeType;
import com.bb.flow.config.node.ContainerConfigNode;
import com.bb.flow.config.node.ContainerInnerNode;
import com.bb.flow.config.node.FunctionConfigNode;
import com.bb.flow.config.node.JudgeConfigNode;
import com.bb.flow.parser.ObjectCreator;
import com.bb.flow.parser.PropertyAssignment;
import com.bb.flow.util.Tools;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

import static com.bb.flow.util.Tools.isEmptyOrNull;

/**
 * @author bigbeard
 * 初始化流程
 * 根据配置信息,创建流程中的各种对象,变量和变量初始化
 */
public class FlowInit {
    protected final Logger logger = LoggerFactory.getLogger(FlowInit.class);

    private boolean createObject = true;

    private ObjectCreator objectCreator;

    private PropertyAssignment propertyAssignment = null;

    public FlowInit() {
        this.objectCreator = new ReflectObjectCreatorImpl();
    }

    public void setCreateObject(boolean createObject) {
        this.createObject = createObject;
    }

    public void setObjectCreator(ObjectCreator objectCreator) {
        this.objectCreator = objectCreator;
    }


    public void setPropertyAssignment(PropertyAssignment propertyAssignment) {
        this.propertyAssignment = propertyAssignment;
    }

    /**
     * 初始化流程
     *
     * @param flowInfo 流程信息
     */
    public void initFlow(FlowInfo flowInfo) throws ParseException {
        if (!createObject) {
            return;
        }
        ConditionInfo condition = flowInfo.getCondition();
        if (null != condition) {
            ClassInfo classInfo = condition.getClassInfo();
            String nName = "流程启动条件";
            if (null != classInfo) {
                ExpressionCalc expressionCalc = createExpressionCalc(classInfo, flowInfo.getFlowTemplateName()
                        , nName);
                condition.setExpressionCalc(expressionCalc);
            }
        }
        Collection<AbstractConfigNode> values = flowInfo.getFlowNodes().values();
        for (AbstractConfigNode x : values) {
            if (x.getNodeType() == NodeType.FUNCTION_NODE) {
                initFunctionNode(flowInfo, x);
            } else if (x.getNodeType() == NodeType.JUDGE_NODE) {
                initJudgeNode(flowInfo, x);
            } else if (x.getNodeType() == NodeType.CONTAINER_NODE) {
                initContainerNode(flowInfo, (ContainerConfigNode) x);
            }
        }
    }

    private void initFunctionNode(FlowInfo flowInfo, AbstractConfigNode x) throws ParseException {
        FunctionConfigNode functionConfigNode = (FunctionConfigNode) x;
        ClassInfo classInfo = functionConfigNode.getClassInfo();
        if (classInfo != null) {
            FlowNode flowNode = createFlowNode(classInfo, flowInfo.getFlowTemplateName(), x.getNodeKey());
            if (null != flowNode) {
                functionConfigNode.setFlowNode(flowNode);
                boolean b = flowNode.initNode();
                if (!b) {
                    logger.warn("初始化流程[{}]中业务对象[{}]函数返回false", flowInfo.getFlowTemplateName(), x.getNodeKey());
                }
            } else {
                String s = String.format("流程[%s]节点[%s]创建对象失败,确保节点实现类[%s]实现了[%s]接口",
                        flowInfo.getFlowTemplateName(), x.getNodeKey(), classInfo.getClassName(), FlowNode.class.getName());
                throw new ParseException(s);
            }
        } else {
            String s = String.format("流程[%s]节点[%s]配置中缺少[%s]接口的实现类",
                    flowInfo.getFlowTemplateName(), x.getNodeKey(), FlowNode.class.getName());
            throw new ParseException(s);
        }
    }

    private void initJudgeNode(FlowInfo flowInfo, AbstractConfigNode x) throws ParseException {
        JudgeConfigNode judgeConfigNode = (JudgeConfigNode) x;
        ClassInfo classInfo = judgeConfigNode.getClassInfo();
        if (null != classInfo) {
            ConditionValueCalc conditionValueCalc = createConditionValueCalc(classInfo, flowInfo.getFlowTemplateName(), x.getNodeKey());
            if (null == conditionValueCalc) {
                String s = String.format("流程[%s]节点[%s]创建对象失败,确保节点实现类实现了[%s]接口",
                        flowInfo.getFlowTemplateName(), x.getNodeKey(), ConditionValueCalc.class.getName());
                throw new ParseException(s);
            }
            judgeConfigNode.setConditionValueCalc(conditionValueCalc);
        } else {
            String s = String.format("流程[%s]节点[%s]配置中缺少[%s]接口的实现类",
                    flowInfo.getFlowTemplateName(), x.getNodeKey(), ConditionValueCalc.class.getName());
            throw new ParseException(s);
        }
        ConditionInfo condition = judgeConfigNode.getCondition();
        if (condition != null) {
            ClassInfo classInfo1 = condition.getClassInfo();
            ExpressionCalc expressionCalc = createExpressionCalc(classInfo1
                    , flowInfo.getFlowTemplateName(), x.getNodeKey());
            if (null == expressionCalc) {
                String s = String.format("流程[%s]节点[%s]创建条件判断对象失败,确保节点实现类实现了[%s]接口",
                        flowInfo.getFlowTemplateName(), x.getNodeKey(), ExpressionCalc.class.getName());
                throw new ParseException(s);
            }
            condition.setExpressionCalc(expressionCalc);
        } else {
            String s = String.format("流程[%s]节点[%s]配置的条件中缺少[%s]接口的实现类",
                    flowInfo.getFlowTemplateName(), x.getNodeKey(), ExpressionCalc.class.getName());
            throw new ParseException(s);
        }
    }

    private void initContainerNode(FlowInfo flowInfo, ContainerConfigNode containerConfigNode) throws ParseException {
        for (ContainerInnerNode innerNode : containerConfigNode.getNodes()) {
            Collection<AbstractConfigNode> values = innerNode.getFlowNodes().values();
            for (AbstractConfigNode x : values) {
                if (x.getNodeType() == NodeType.CONTAINER_NODE) {
                    initContainerNode(flowInfo, (ContainerConfigNode) x);
                } else if (x.getNodeType() == NodeType.JUDGE_NODE) {
                    initJudgeNode(flowInfo, x);
                } else if (x.getNodeType() == NodeType.FUNCTION_NODE) {
                    initFunctionNode(flowInfo, x);
                }
            }
        }
    }

    private FlowNode createFlowNode(ClassInfo classInfo, String flowTemplateName, String nodeKey) {
        return create(classInfo, FlowNode.class, flowTemplateName, nodeKey);
    }

    private ExpressionCalc createExpressionCalc(ClassInfo classInfo, String flowTemplateName, String nodeKey) {
        return create(classInfo, ExpressionCalc.class, flowTemplateName, nodeKey);
    }

    private ConditionValueCalc createConditionValueCalc(ClassInfo classInfo, String flowTemplateName, String nodeKey) {
        return create(classInfo, ConditionValueCalc.class, flowTemplateName, nodeKey);
    }

    @SuppressWarnings("unchecked")
    private <T> T create(ClassInfo classInfo, Class<T> cls, String flowTemplateName, String nodeKey) {
        if (!isEmptyOrNull(classInfo.getClassName())) {
            try {
                Object o = objectCreator.instanceBean(classInfo.getClassName());
                if (null != o) {
                    if (Tools.testClassImplementsInterface(o.getClass(), cls)) {
                        if (null != this.propertyAssignment) {
                            Map<String, String> properties = new HashMap<>(classInfo.getProperties());
                            boolean b = this.propertyAssignment.setValueToObject(o, properties);
                            if (!b) {
                                logger.error("流程[{}]节点[{}]初始化对象[{}]的属性时返回失败.", flowTemplateName, nodeKey,
                                        classInfo.getClassName());
                            }
                        } else {
                            classInfo.getProperties().forEach((key, value) -> {
                                        if (key != null && value != null) {
                                            boolean b = Tools.setValueToObject(o, key, value);
                                            if (!b) {
                                                logger.error("流程[{}]节点[{}]给对象[{}]的属性[{}]赋值[{}]失败",
                                                        flowTemplateName, nodeKey, classInfo.getClassName(), key, value);
                                            }
                                        } else {
                                            logger.error("流程[{}]节点[{}]对象[{}]配置文件对应的属性或者值为null,无法赋值",
                                                    flowTemplateName, nodeKey, classInfo.getClassName());
                                        }
                                    }
                            );
                        }
                        if (cls.isInstance(o)) {
                            return (T) o;
                        } else {
                            logger.error("通过创建器获取到的节点实例不是流程模版[{}]中节点[{}]的类[{}]的实例",
                                    flowTemplateName, nodeKey, classInfo.getClassName());
                        }
                    } else {
                        logger.error("流程模版[{}]中节点[{}]中的类[{}]未实现{}",
                                flowTemplateName, nodeKey, classInfo.getClassName(), cls.getName());
                    }
                }
            } catch (Exception ex) {
                logger.error("创建流程[{}]节点[{}]对象实例发生异常，异常信息：{}；可能是流程模版[{}]中节点[{}]中的类[{}]未实现{}",
                        flowTemplateName, nodeKey, ex.getMessage(), flowTemplateName, nodeKey,
                        classInfo.getClassName(), cls.getName());
            }
        }
        return null;
    }
}
